package tcpserver

import (
	"bufio"
	"crypto/tls"
	"errors"
	"github.com/astaxie/beego/logs"
	"log"
	"net"
	"time"
)

// Client holds info about connection
type Client struct {
	conn       net.Conn
	Server     *server
	HeartTimer *time.Timer //心跳定时器
	TmpBuffer  []byte
}

// TCP server
type server struct {
	address                  string // Address to open connection: localhost:9999
	config                   *tls.Config
	onNewClientCallback      func(c *Client)
	onClientConnectionClosed func(c *Client, err error)
	onNewMessage             func(c *Client, message []byte)
}

// Read client data from channel
func (c *Client) listen() {
	defer func() {
		if r := recover(); r != nil {
			logs.Error("panic: %v", r)
		}
	}()
	c.Server.onNewClientCallback(c)
	reader := bufio.NewReader(c.conn)
	for {
		message := make([]byte, 4096)
		n, err := reader.Read(message)
		if err != nil {
			_ = c.Close(err)
			return
		}
		if len(message) == 0 || message == nil {
			logs.Error("信息无效")
			continue
		}
		c.Server.onNewMessage(c, message[0:n])
	}
}

// Send text message to client
func (c *Client) Send(message []byte) error {
	_, err := c.conn.Write(message)
	if err != nil {
		_ = c.Close(err)
		return err
	}
	return nil
}

func (c *Client) Conn() net.Conn {
	return c.conn
}

func (c *Client) SetKeepAlive(period time.Duration) error {
	// 开启 TCP Keep-Alive 机制
	tcpConn, ok := c.conn.(*net.TCPConn)
	if !ok {
		err := errors.New("cannot convert to tcp conn")
		_ = c.Close(err)
		return err
	}
	if err := tcpConn.SetKeepAlive(true); err != nil {
		_ = c.Close(err)
		return err
	}
	if err := tcpConn.SetKeepAlivePeriod(period); err != nil {
		_ = c.Close(err)
		return err
	}
	return nil
}

func (c *Client) Close(err error) error {
	c.Server.onClientConnectionClosed(c, err)
	return c.conn.Close()
}

// OnNewClient Called right after server starts listening new client
func (s *server) OnNewClient(callback func(c *Client)) {
	s.onNewClientCallback = callback
}

// OnClientConnectionClosed Called right after connection closed
func (s *server) OnClientConnectionClosed(callback func(c *Client, err error)) {
	s.onClientConnectionClosed = callback
}

// OnNewMessage Called when Client receives new message
func (s *server) OnNewMessage(callback func(c *Client, message []byte)) {
	s.onNewMessage = callback
}

// Listen starts network server
func (s *server) Listen() {
	defer func() {
		if r := recover(); r != nil {
			logs.Error("panic: %v", r)
		}
	}()
	var listener net.Listener
	var err error
	if s.config == nil {
		listener, err = net.Listen("tcp", s.address)
	} else {
		listener, err = tls.Listen("tcp", s.address, s.config)
	}
	if err != nil {
		log.Fatal("Error starting TCP server.", err.Error())
	}
	defer listener.Close()
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal(err)
		}
		client := &Client{
			conn:   conn,
			Server: s,
		}
		go client.listen()
	}
}

// New Creates new tcp server instance
func New(address string) *server {
	logs.Info("Creating server with address", address)
	server := &server{
		address: address,
		config:  nil,
	}

	server.OnNewClient(func(c *Client) {})
	server.OnNewMessage(func(c *Client, message []byte) {})
	server.OnClientConnectionClosed(func(c *Client, err error) {})

	return server
}

func NewWithTLS(address string, certFile string, keyFile string) *server {
	log.Println("Creating server with address", address)
	cert, _ := tls.LoadX509KeyPair(certFile, keyFile)
	/*#nosec*/
	config := tls.Config{
		Certificates: []tls.Certificate{cert},
	}
	server := &server{
		address: address,
		config:  &config,
	}

	server.OnNewClient(func(c *Client) {})
	server.OnNewMessage(func(c *Client, message []byte) {})
	server.OnClientConnectionClosed(func(c *Client, err error) {})

	return server
}
